# -*- coding:utf-8 -*-
import os
import sys
from tempfile import TemporaryDirectory
from src.utils import runcmd
from src.core.oetype import ArchType
from src.oerebuild_tmpl import OerebuildTmplateRunner
from src.utils.logger import logger


class RebuildISOWithPackage(object):
    def __init__(self, iso, rpms, work_dir, output,  arch,
                 replace_dir, tmpl="", work_tmp="/var/tmp"):
        """
        :param iso: iso path, is a abs path not relative path
        :param rpms: rpms path, abs path
        :param work_dir: work path, abs path
        :param output: output path, abs path
        :param tmpl: tmpl path, abs path
        :param work_tmp: work tmp path for mount iso, abs path
        """
        self._iso = iso
        self._rpms = rpms
        self._work_dir = work_dir
        self._output = output
        self._work_tmp = work_tmp
        self._tmpl = tmpl
        self._replace_dir = replace_dir
        self._arch = arch
        self._product_name = ""
        self._dependence_list = {"createrepo": "createrepo_c",
                                "mkisofs": "mkisofs",
                                "implantisomd5" : "isomd5sum"}

    def do_iso_rebuild(self):
        """
        主要处理流程，出现异常直接中断
        :return:NA
        """
        self.mount_and_cp()
        self.read_product_name()
        self.replace_rpms()
        self.create_repo_group()
        self.make_iso()
        self.implant_iso_md5()

    def replace_in_minios(self):
        # unsquashfs
        install_img_path = self._work_dir + "/images/install.img"
        install_mount_path = os.path.join("/var/tmp", "install_")
        squashfs_path = self._work_dir + "/images/squashfs-root"
        rootfs_path = squashfs_path + "/LiveOS/rootfs.img"
        if runcmd.unsquashfs(install_img_path, squashfs_path) != 0:
            error_message = "Unsquashfs image Failed! image path: {}".\
                format(install_img_path)
            logger.error(error_message)
            print(error_message)
            sys.exit(0)
        logger.info("Unsquashfs successfully!")
        # mount
        with TemporaryDirectory(prefix=install_mount_path, dir="/var/tmp") as install_dir:
            if runcmd.mount(rootfs_path, install_dir, "ext4", "rw") != 0:
                logger.error("Failed to mount %s to %s", rootfs_path, install_dir)
                sys.exit(0)
            logger.info("install.img mounted on dir %s", install_dir)
            if os.path.exists(self._replace_dir) is True:
                filelist = os.listdir(self._replace_dir)
                for file in filelist:
                    file_path = os.path.join(self._replace_dir, file)
                    if os.path.isdir(file_path):
                        runcmd.cp_catalog(file_path, install_dir)
                    if os.path.isfile(file_path):
                        runcmd.cp_file(src_file, install_dir)
            if self._tmpl == "":
                # use dnf install
                for root, dirs, files in os.walk(self._rpms):
                    for file in files:
                        src_file = os.path.join(root, file)
                        if src_file.endswith(".rpm") is False:
                            continue
                        if runcmd.rpm_setup(src_file, install_dir) != 0:
                            logger.error("Set up rpm package %s to %s failed!", src_file, install_dir)
                            sys.exit(0)
                        logger.info("Set up rpm package %s successfully!", src_file)
            else:
                # use tmpl command
                runner = OerebuildTmplateRunner(self._tmpl, self._rpms, install_dir)
                runner.run()

            # umount + squashfs
            if runcmd.umount(install_dir) != 0:
                logger.error("Umount %s Failed!", install_dir)
                sys.exit(0)
            if runcmd.mksquashfs(squashfs_path, install_img_path) != 0:
                logger.error("Mksquashfs %s Failed!", squashfs_path)
                sys.exit(0)
        # delete
        if runcmd.remove_file(squashfs_path) != 0:
            logger.error("Remove file %s failed!", squashfs_path)
            sys.exit(0)

    def replace_repo(self):
        new_repo_path = self._work_dir + "/Packages"
        for root, dirs, files in os.walk(self._rpms):
            for file in files:
                src_file = os.path.join(root, file)
                if src_file.endswith(".rpm") is False:
                    continue
                dest_file = os.path.join(new_repo_path, file)
                if runcmd.cp_file(src_file, dest_file) != 0:
                    logger.error("Copy file %s to %s Failed", src_file, dest_file)
                    sys.exit(0)

    def mount_and_cp(self):
        print("We are mounting iso, and copy all files, please wait...", )
        logger.info("begin mount iso and copy files")
        iso_mount_path = os.path.join(self._work_tmp, "iso_")
        try:
            with TemporaryDirectory(prefix=iso_mount_path, dir=self._work_tmp) as iso_dir:
                if runcmd.mount(self._iso, iso_dir, "iso9660", "loop"):
                    error_message = "Failed to mount {} to {}".format(self._iso, iso_dir)
                    logger.error(error_message)
                    print(error_message)
                    sys.exit(0)
                logger.info("ISO mounted on dir: %s", iso_dir)

                if runcmd.cp_iso_file(iso_dir, self._work_dir):
                    error_message = "Failed to cp iso file from {} to {}".\
                        format(iso_dir, self._work_dir)
                    logger.error(error_message)
                    print(error_message)
                    sys.exit(0)
                logger.info("CP all iso files successfully!")

                if runcmd.umount(iso_dir):
                    error_message = "Failed to umount dior {}".format(iso_dir)
                    logger.error(error_message)
                    print(error_message)
                    sys.exit(0)
                logger.info("umount iso successfully")
                logger.info("Mount iso and copy file successfully!")
        except OSError as err:
            logger.error(err)
            print(error)
            sys.exit(0)

    def read_product_name(self):
        tree_info_file = os.path.join(self._work_dir, ".treeinfo")
        with open(tree_info_file) as tree_info:
            for content in tree_info:
                if content.startswith("name"):
                    self._product_name = content.split("=")[1].strip()
                    logger.info("product name is {}".format(self._product_name))

    def replace_rpms(self):
        # mount install.img
        logger.info("begin replace rpms")
        print("begin replace rpms, please wait...")
        self.replace_in_minios()
        # replace repo
        self.replace_repo()
        logger.info("Replace rpms for new iso successfully!")

    def implant_iso_md5(self):
        if runcmd.implant_iso_md5(self._output) != 0:
            logger.error("Implant iso md5 Failed!")
            sys.exit(0)
        logger.info("implant iso md5 successfully!")

    def create_repo_group(self):
        # recreaterepo repo for iso
        if runcmd.createrepo_group(self._work_dir) != 0:
            logger.error("Failed to createrepo for dir: %s", self._work_dir)
            sys.exit(0)
        logger.info("Createrepo for new iso successfully!")

    def make_iso(self):
        # mkisofs for new iso
        # vid = openEuler-20.03-lts-SP1-x86_64
        # product_name = openEuler
        # version_info = 20.03-lts-SP1
        # arch = x86_64
        if self._product_name == "":
            logger.error("failed to get product name!")
            print("failed to get product name! has .treeinfo in iso?")
            sys.exit(0)
        vid = self._product_name + "-" + self._arch.value
        if runcmd.mkisofs(self._work_dir, self._output, self._arch, vid) != 0:
            logger.error("Failed to rebuild iso")
            sys.exit(0)
        logger.info("ISO rebuild successfully, please check: %s", self._output)

    def check_dependence(self):
        for cmd in self._dependence_list:
            if runcmd.exist_command(cmd) is False:
                error_message = "miss command {}, please run 'dnf install -y {}'".\
                    format(cmd, self._dependence_list[cmd])
                logger.error(error_message)
                print(error_message)
                sys.exit(0)

    def check_diskfree(self):
        vfs = os.statvfs(self._work_dir)
        left = vfs.f_bavail * vfs.f_frsize / 1024 / 1024 / 1024
        logger.info("left space is {}".format(left))
        if left < 20:
            error_message = "disk free is not enough, need 20G," \
                            " left size is {}".format(left)
            logger.error(error_message)
            print(error_message)
            sys.exit(0)
